﻿/* The tests in this class are adapted from the original Lucandra project.
 * https://github.com/tjake/Lucandra/tree/master/test/lucandra/lucandraTests.java */

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Apache.Cassandra;
using Lucene.Net.Search;
using Lucene.Net.QueryParsers;
using Lucene.Net.Index;
using System.Threading;
using Lucene.Net.Highlight;
using Lucandra.Net.Connection.Pooling;
using Lucandra.Convenience;

namespace Lucandra.Net.Tests
{
    [TestClass]
    public class LucandraTests
    {
        private static string keyspaceName;
        private static string indexName;
        private static Analyzer analyzer;
        private static string text;
        private static string highlightedText;
        private static LucandraWriter indexWriter;
        private static int documentID;

        static LucandraTests()
        {
            indexName = "Test Index";
            analyzer = new CJKAnalyzer();
            text = "this is an example value foobar foobar";
            highlightedText = "this is an example value <B>foobar</B> <B>foobar</B>";
            indexWriter = new LucandraWriter(indexName);
            documentID = 0;
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        [ClassCleanup]
        public static void Cleanup()
        {
            using (var client = CassandraConnectionPool.Borrow())
            {
            }
        }

        [TestMethod]
        public void TestCreateHash()
        {
            byte[] b = new byte[] { 1, 27, 31, 150, 212, 76, 13, 97, 132, 189 };
            string hash = LucandraHelper.CreateHash(b);
            string hash2 = LucandraHelper.CreateHash(b);
            Assert.AreEqual(hash, hash2);

            b = b.Reverse().ToArray();
            hash = LucandraHelper.CreateHash(b);
            hash2 = LucandraHelper.CreateHash(b);
            Assert.AreEqual(hash, hash2);
        }

        [TestMethod]
        public void TestWriter()
        {
            var doc1 = new Document();
            doc1.Add(new Field("key", text, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
            indexWriter.AddDocument(doc1, analyzer);

            var doc2 = new Document();
            doc2.Add(new Field("key", "this is another example", Field.Store.YES, Field.Index.ANALYZED));
            indexWriter.AddDocument(doc2, analyzer);

            // Index 100 documents to test order.
            for (int i = 300; i >= 200; i--)
            {
                var doc = new Document();
                doc.Add(new Field("key", "sort this", Field.Store.YES, Field.Index.ANALYZED));
                doc.Add(new Field("date", "test" + i, Field.Store.YES, Field.Index.NOT_ANALYZED));
                indexWriter.AddDocument(doc, analyzer);
            }

            // Unicode document.
            var doc3 = new Document();
            doc3.Add(new Field("key", "\u5639\u563b", Field.Store.YES, Field.Index.ANALYZED));
            doc3.Add(new Field("key", "samefield", Field.Store.YES, Field.Index.ANALYZED));
            doc3.Add(new Field("url", "http://www.google.com", Field.Store.YES, Field.Index.NOT_ANALYZED));
            indexWriter.AddDocument(doc3, analyzer);

            Thread.Sleep(3000);
        }

        [TestMethod]
        public void TestUnicode()
        {
            //var reader = new IndexReader2(indexName, client);
            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);

            var qp = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "key", analyzer);
            var query = qp.Parse("+key:\u5639\u563b");

            var docs = searcher.Search(query, 10);

            Assert.AreEqual(1, docs.totalHits);

            var doc = searcher.Doc(docs.scoreDocs[0].doc);

            Assert.IsNotNull(doc.GetField("key"));
        }

        [TestMethod]
        public void TestMultiValuedFields()
        {
            //var reader = new IndexReader2(indexName, client);
            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);

            var qp = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "key", analyzer);
            var query = qp.Parse("+key:samefield");

            var docs = searcher.Search(query, 10);

            Assert.AreEqual(1, docs.totalHits);

            var doc = searcher.Doc(docs.scoreDocs[0].doc);

            Field[] fields = doc.GetFields("key");
            string[] tests = new string[] { "\u5639\u563b", "samefield" };

            Assert.AreEqual(2, fields.Length);

            for (int i = 0; i < fields.Length; i++)
                Assert.AreEqual(tests[i], fields[i].StringValue());
        }

        [TestMethod]
        public void TestKeywordField()
        {
            //var reader = new IndexReader2(indexName, client);
            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);

            var tq = new TermQuery(new Term("url", "http://www.google.com"));
            var docs = searcher.Search(tq, 10);

            Assert.AreEqual(1, docs.totalHits);
        }

        [TestMethod]
        public void TestDelete()
        {
            indexWriter.DeleteDocuments(new Term("key", "\u5639\u563b"));
            //var reader = new IndexReader2(indexName, client);
            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);

            var qp = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "key", analyzer);
            var query = qp.Parse("+key:\u5639\u563b");

            var docs = searcher.Search(query, 10);

            Assert.AreEqual(0, docs.totalHits);
        }

        [TestMethod]
        public void TestSearch()
        {
            //var reader = new IndexReader2(indexName, client);
            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);

            var qp = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "key", analyzer);
            var query = qp.Parse("+key:another");

            var docs = searcher.Search(query, 10);

            Assert.AreEqual(1, docs.totalHits);

            var doc = searcher.Doc(docs.scoreDocs[0].doc);

            Assert.IsNotNull(doc.GetField("key"));
        }

        [TestMethod]
        public void TestScore()
        {
            //var reader = new IndexReader2(indexName, client);
            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);

            var qp = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "key", analyzer);
            var query = qp.Parse("+key:example");

            var docs = searcher.Search(query, 10);

            Assert.AreEqual(2, docs.totalHits);

            var doc = searcher.Doc(docs.scoreDocs[0].doc);
            var doc2 = searcher.Doc(docs.scoreDocs[1].doc);

            string field = doc.GetField("key").StringValue();

            Assert.AreEqual("this is another example", field);
        }

        [TestMethod]
        public void TestMissingQuery()
        {
            //var reader = new IndexReader2(indexName, client);
            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);

            var qp = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "key", analyzer);
            var query = qp.Parse("+key:bogus");

            var docs = searcher.Search(query, 10);

            Assert.AreEqual(0, docs.totalHits);
        }

        [TestMethod]
        public void TestWildcardQuery()
        {
            //var reader = new IndexReader2(indexName, client);
            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);

            var qp = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "key", analyzer);
            var query = qp.Parse("+key:anoth*");

            var docs = searcher.Search(query, 10);

            Assert.AreEqual(1, docs.totalHits);

            var doc = reader.Document(docs.scoreDocs[0].doc);

            string val = doc.Get("key");
            Assert.IsTrue(val == "this is another example");

            query = qp.Parse("+date:test*");
            docs = searcher.Search(query, 10);

            Assert.AreEqual(101, docs.totalHits);
        }

        [TestMethod]
        public void TestSortQuery()
        {
            //var reader = new IndexReader2(indexName, client);
            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);

            var sort = new Sort(new SortField("date", SortField.STRING));

            var qp = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "key", analyzer);
            var query = qp.Parse("+key:sort");

            var docs = searcher.Search(query, null, 10, sort);

            for (int i = 0; i < 10; i++)
            {
                var d = reader.Document(docs.scoreDocs[i].doc);
                var dval = d.Get("date");
                Assert.AreEqual("test" + (i + 200), dval);
            }
        }

        [TestMethod]
        public void TestRangeQuery()
        {
            //var reader = new IndexReader2(indexName, client);
            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);

            var qp = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "key", analyzer);
            var query = qp.Parse("+key:[apple TO zoo]");

            var docs = searcher.Search(query, 10);

            Assert.AreEqual(103, docs.totalHits);
        }

        [TestMethod]
        public void TestExactQuery()
        {
            //var reader = new IndexReader2(indexName, client);
            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);
            var qp = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "key", analyzer);

            var query = qp.Parse("+key:\"foobar foobar\"");
            var docs = searcher.Search(query, 10);
            Assert.AreEqual(1, docs.totalHits);

            query = qp.Parse("+key:\"not in index\"");
            docs = searcher.Search(query, 10);
            Assert.AreEqual(0, docs.totalHits);

            query = qp.Parse("+key:\"is an\"");
            docs = searcher.Search(query, 10);
            Assert.AreEqual(1, docs.totalHits);
        }

        [TestMethod]
        public void TestSimpleAnalyzerWriteRead()
        {
            var doc = new Document();
            doc.Add(new Field("title", text, Field.Store.YES, Field.Index.ANALYZED));
            indexWriter.AddDocument(doc, analyzer);

            //var reader = new IndexReader2(indexName, client);
            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);
            var qp = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "title", analyzer);

            var q = qp.Parse("foobar");
            var docs = searcher.Search(q, 10);
            Assert.AreEqual(1, docs.totalHits);

            q = qp.Parse("\"not in index\"");
            docs = searcher.Search(q, 10);
            Assert.AreEqual(0, docs.totalHits);

            reader.Reopen();

            q = qp.Parse("\"foobar foobar\"");
            docs = searcher.Search(q, 10);
            Assert.AreEqual(0, docs.totalHits);
        }

        [TestMethod]
        public void TestHighlight()
        {
            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);

            var qp = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "key", analyzer);
            var query = qp.Parse("+key:\"foobar foobar\"");
            var docs = searcher.Search(query, 10);
            Assert.AreEqual(1, docs.totalHits);

            var formatter = new SimpleHTMLFormatter();
            var scorer = new QueryScorer(query, "key");
            var highlighter = new Highlighter(formatter, scorer);
            highlighter.SetTextFragmenter(new SimpleFragmenter(int.MaxValue));

            var tvStream = TokenSources.GetTokenStream(reader, docs.scoreDocs[0].doc, "key");
            var rv = highlighter.GetBestFragment(tvStream, text);

            Assert.IsNotNull(rv);
            Assert.AreEqual(highlightedText, rv);
        }

        //[TestMethod]
        //public void TestLucandraFilter()
        //{
        //    //var reader = new IndexReader2(indexName, client);
        //    var reader = new LucandraReader(indexName);
        //    var searcher = new IndexSearcher(reader);

        //    for (int i = 0; i < 10; i++)
        //    {
        //        var doc = new Document();
        //        doc.Add(new Field("aKey", "aKey" + i, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
        //        doc.Add(new Field("category", "category1", Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
        //        indexWriter.AddDocument2(doc, analyzer);
        //    }

        //    var qp = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "aKey", analyzer);

        //    var filter = new LucandraFilter2();
        //    filter.AddTerm(new Term("category", "category1"));

        //    var q = qp.Parse("aKey1");
        //    var docs = searcher.Search(q, filter, 10);
        //    Assert.AreEqual(1, docs.totalHits);

        //    reader.Reopen();

        //    q = qp.Parse("aKey1 OR aKey2");
        //    docs = searcher.Search(q, filter, 10);
        //    Assert.AreEqual(2, docs.totalHits);

        //    reader.Reopen();

        //    q = qp.Parse("[aKey0 TO aKey5]");
        //    docs = searcher.Search(q, filter, 10);
        //    Assert.AreEqual(6, docs.totalHits);

        //    reader.Reopen();

        //    filter = new LucandraFilter2();
        //    filter.AddTerm(new Term("category", "category0"));

        //    docs = searcher.Search(q, filter, 10);
        //    Assert.AreEqual(0, docs.totalHits);
        //}

        [TestMethod]
        public void TestLucandraTermDocs()
        {
            var indexWriter = new LucandraWriter(indexName);
 
            int docCount = 100;

            for (int i = 0; i < docCount; i++) 
            {
                var doc = new Document();

                var f1 = new Field("UUID", "UUID" + i,
                        Field.Store.NO,
                        Field.Index.NOT_ANALYZED_NO_NORMS);
 
                var f2 = new Field("parent", "parenta",
                        Field.Store.NO,
                        Field.Index.NOT_ANALYZED_NO_NORMS);
 
                var f3 = new Field("nodeType", "item",
                        Field.Store.NO,
                        Field.Index.NOT_ANALYZED_NO_NORMS);

                doc.Add(f1);
                doc.Add(f2);
                doc.Add(f3);
 
                indexWriter.AddDocument(doc, analyzer);
            }
 
            var tq = new TermQuery(new Term("parent", "parenta"));
            var tq1 = new TermQuery(new Term("nodeType", "item"));

            var query = new BooleanQuery();
            query.Add(tq, BooleanClause.Occur.MUST);
            query.Add(tq1, BooleanClause.Occur.MUST);

            var indexReader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(indexReader);
 
            var topDocs = searcher.Search(query, 1000);

            Assert.AreEqual(docCount, topDocs.totalHits);
        }

        [TestMethod]
        public void TestAutoCommit()
        {
            var writer = new LucandraWriter(indexName, true);
            var doc = new Document();
            doc.Add(new Field("autocommit", "true", Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS));
            writer.AddDocument(doc, analyzer);

            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);
            var docs = searcher.Search(new TermQuery(new Term("autocommit", "true")), 10);
            Assert.IsTrue(docs.totalHits == 1);
        }

        [TestMethod]
        public void TestManualCommit()
        {
            var writer = new LucandraWriter(indexName, false);
            var doc = new Document();
            doc.Add(new Field("autocommit", "false", Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS));
            writer.AddDocument(doc, analyzer);

            var reader = new LucandraReader(indexName);
            var searcher = new LucandraSearcher(reader);
            var docs = searcher.Search(new TermQuery(new Term("autocommit", "false")), 10);
            Assert.IsTrue(docs.totalHits == 0);

            writer.Commit();

            docs = searcher.Search(new TermQuery(new Term("autocommit", "false")), 10);
            Assert.IsTrue(docs.totalHits == 1);
        }
    }
}
